﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Entities;
using DataAccess;

namespace Controller
{
    public class UploadRelationalTableController
    {
        #region Events

        public event UploadRelationalTableEventHandler UploadRelationalTableEventCompleted;
        public event UploadRelationalTableEventHandler UploadRowEventCompleted;
        public event UploadRelationalTableEventHandler ProgressChangedEventCompleted;

        #endregion

        #region Privates
        private string failureReason;
        private CloudStorage cloudStorage;

        private System.ComponentModel.BackgroundWorker backgroundWorker;
       

        private string EntityXmlTemplate = @"<?xml version=""1.0"" encoding=""utf-8"" standalone=""yes""?>
        <entry 
                xmlns:d=""http://schemas.microsoft.com/ado/2007/08/dataservices"" 
                xmlns:m=""http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"" 
                xmlns=""http://www.w3.org/2005/Atom"">
            <title />
            <updated>{0:yyyy-MM-ddTHH:mm:ss.fffffffZ}</updated>
            <author>
                <name />
            </author>
            <id />
            <content type=""application/xml"">
              <m:properties>
                 {1}
              </m:properties>
            </content>
        </entry>";

        private string EntityPropertyTemplate =
            @"<d:{0} m:type=""Edm.{1}"">{2}</d:{0}>";

        private string EntityPropertyTemplateRowKeyPartitionKey =
            @"<d:{0}>{1}</d:{0}>";

        private string EntityPropertyTemplateNull =
            @"<d:{0} m:type=""Edm.{1}"" m:null=""true""/>";

        private EAVTable EavTableObject;

        private string AccountName;

        private string AccountKey;

        private string EndPoint;

        private string TableName;

        #endregion

        
        public void AbortProcess()
        {
            try
            {
                this.backgroundWorker.CancelAsync();
            }
            catch (Exception)
            {
            }
        }

        public UploadRelationalTableController()
        {
            backgroundWorker = new System.ComponentModel.BackgroundWorker();
            backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(backgroundWorker_DoWork);
            backgroundWorker.WorkerSupportsCancellation = true;
        }

        private void OnProgressChangedEventCompleted(UploadRelationalTableEventArgs e)
        {
            if (ProgressChangedEventCompleted != null)
            {
                ProgressChangedEventCompleted(e);
            }
        }

        private void OnUploadRelationalTableEventCompleted(UploadRelationalTableEventArgs e)
        {
            if (UploadRelationalTableEventCompleted != null)
            {
                UploadRelationalTableEventCompleted(e);
            }
        }

        private void OnUploadRowEventCompleted(UploadRelationalTableEventArgs e)
        {
            if (UploadRowEventCompleted != null)
            {
                UploadRowEventCompleted(e);
            }
        }


        void backgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            GenerateXMLRowByRow(EavTableObject, AccountName, AccountKey, EndPoint,TableName);

            if (this.backgroundWorker.CancellationPending)
            {
                e.Cancel = true;
            } 
        }


        public void GenerateXMLRowByRowAsync(EAVTable EavTableObject, string AccountName, string AccountKey, string EndPoint,string TableName)
        {
            this.EavTableObject = EavTableObject;
            this.EndPoint = EndPoint;
            this.AccountKey = AccountKey;
            this.AccountName = AccountName;
            this.TableName = TableName;

            if (backgroundWorker.IsBusy)
                backgroundWorker.CancelAsync();

            backgroundWorker.RunWorkerAsync();
        }

        public void GenerateXMLRowByRow(EAVTable EavTableObject, string AccountName, string AccountKey, string EndPoint, string TableName)
        {
            cloudStorage = new CloudStorage();
            
            StringBuilder OneRowXML = new StringBuilder();
            string RowXML = String.Empty;
            int ColumnCount = EavTableObject.EavTable.Columns.Count;
            int RowCount = EavTableObject.EavTable.Rows.Count;

            for (int i = 0; i < RowCount; i++)
            {
                DataRow row = EavTableObject.EavTable.NewRow();

                row = EavTableObject.EavTable.Rows[i];

                OneRowXML.Remove(0, OneRowXML.Length);
                RowXML.Remove(0, RowXML.Length);

                for (int j = 0; j < ColumnCount; j++)
                {
                    if (j == 0 || j == 1)
                        OneRowXML.Append(string.Format(EntityPropertyTemplateRowKeyPartitionKey, EavTableObject.EavTable.Columns[j].ColumnName.ToString(),  XMLSafeString(row[j].ToString())));
                    else
                    {
                        if (!string.IsNullOrEmpty(row[j].ToString()))
                            OneRowXML.Append(string.Format(EntityPropertyTemplate, EavTableObject.EavTable.Columns[j].ColumnName.ToString(), EavTableObject.ColumnList[j].Type1, FormatPropertyValue(EavTableObject.ColumnList[j].Type1, row[j].ToString())));
                        else
                            OneRowXML.Append(string.Format(EntityPropertyTemplateNull, EavTableObject.EavTable.Columns[j].ColumnName.ToString(), EavTableObject.ColumnList[j].Type1));
                    }
                }
                RowXML = String.Format(EntityXmlTemplate, DateTime.UtcNow, OneRowXML.ToString());
                try
                {
                    if (this.backgroundWorker.CancellationPending)
                    {
                        OnUploadRelationalTableEventCompleted(new UploadRelationalTableEventArgs("Aborted by user.") { Abort=true});
                        return;
                    }
                        cloudStorage.UploadTable(RowXML, AccountName, AccountKey, EndPoint, TableName);

                        OnUploadRowEventCompleted(new UploadRelationalTableEventArgs() { RowNumber = i, Progress = (int)(((double)(i+1) / (double)RowCount) * (100)) });
                }
                catch (Exception ex)
                {
                    if (failureReason != string.Empty)
                    {
                        OnUploadRowEventCompleted(new UploadRelationalTableEventArgs(new Exception(failureReason)) { RowNumber = i, Progress = (int)(((double)(i+1) / (double)RowCount) * (100)) });
                        failureReason = string.Empty;
                    }
                    else
                        OnUploadRowEventCompleted(new UploadRelationalTableEventArgs(ex) { RowNumber = i, Progress = (int)(((double)(i+1) / (double)RowCount) * (100)) });
                }
            }
            OnUploadRelationalTableEventCompleted(new UploadRelationalTableEventArgs("Records uploading finished."));
        }

        private string FormatPropertyValue(string PropertyType, string PropertyValue)
        {
            string ReturnPropertyValue = PropertyValue;
            try
            {
                switch (PropertyType)
                {
                    case "Boolean":
                        ReturnPropertyValue = Convert.ToBoolean(PropertyValue).ToString();
                        break;
                    case "DateTime":
                        ReturnPropertyValue = Convert.ToDateTime(PropertyValue).ToString("yyyy-MM-ddTHH:mm:ss");
                        break;
                    case "Int32":
                        ReturnPropertyValue = Convert.ToInt32(PropertyValue).ToString();
                        break;
                    case "Int64":
                        ReturnPropertyValue = Convert.ToInt64(PropertyValue).ToString();
                        break;
                    case "Double":
                        ReturnPropertyValue = Convert.ToDouble(PropertyValue).ToString();
                        break;
                    case "String":
                        ReturnPropertyValue = XMLSafeString(PropertyValue);
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                failureReason = ex.Message;
            }
            return ReturnPropertyValue;
        }

        private string XMLSafeString(string inputString)
        {
            inputString = inputString.Replace("&", "&amp;");
            inputString = inputString.Replace("<", "&lt;");
            inputString = inputString.Replace(">", "&gt;");
            inputString = inputString.Replace("\"", "&quot;");
            inputString = inputString.Replace("'", "&apos;");
            return inputString;
        }
    }
}
